package MT::StateMgr;
use strict;

use base 'MT::ErrorHandler';

use vars qw( $VERSION );
$VERSION = 0.31;

use MT::Session;

# Eventually take in cookie management routines?

sub new {
    my $class = shift;
    my $self = bless {}, $class;
    $self->init(@_);
}

sub init {
    my $self = shift;
    $self->{cfg} = shift;

    # defaults
    $self->{cfg}->{kind}        ||= 'SI';
    $self->{cfg}->{cookies}     ||= 1;
    $self->{cfg}->{cookie_name} ||= 'MTSID';
    $self->{cfg}->{cookie_path} ||= '/';
    $self->{cfg}->{timeout}     ||= 3600;
    $self->{cfg}->{sig_window}  ||= 600;
    $self;
}

sub config      { \%{$_[0]->{cfg}} }
sub kind        { $_[0]->{cfg}->{kind} }
sub cookie_name { $_[0]->{cfg}->{cookie_name} }
sub cookie_path { $_[0]->{cfg}->{cookie_path} }
sub timeout     { $_[0]->{cfg}->{timeout} }
sub sig_window  { $_[0]->{cfg}->{sig_window} }

sub create_session {
    my ($mgr, $session) = @_;
    $session ||= {};
    my $app = MT->instance;
    if (ref $session eq 'HASH') {
        my $s = MT::Session->new;
        $s->set_values($session);
        $session = $s;
    }
    $session->id($mgr->get_token) unless $session->id;
    $session->start(time)         unless $session->start;
    $session->kind($mgr->{cfg}->{kind});
    $session->save
      or
      return $mgr->error("Session could not be created: " . $session->errstr);
    if ($mgr->{cfg}->{cookies}) {
        my %cookie = (
                      -name    => $mgr->{cfg}->{cookie_name},
                      -value   => $session->id,
                      -path    => $mgr->{cfg}->{cookie_path},
                      -expires => '+1h'                         # ????
        );
        $app->bake_cookie(%cookie);
    }
    MT->run_callbacks('SessionCreated', $mgr, $session);
    $session;
}

sub get_session {
    my $mgr = shift;
    my $app = MT->instance;
    my $key;
    if ($mgr->{cfg}->{cookies}) {
        my %cookies = $app->cookies;
        return undef unless ($cookies{$mgr->{cfg}->{cookie_name}});
        $key = $cookies{$mgr->{cfg}->{cookie_name}}->value || '';
    } else {
        $key = shift;
    }
    $key =~ y/+/ /;
    my $session = MT::Session->load($key);    # {id=>$key}
    my $timeout = $mgr->timeout
      || MT->config->CommentSessionTimeout
      || $mgr->{cfg}->{sig_window};
    $session = $mgr->expire_session($session)
      if (!$session || ($session->start + $timeout < time));
    $session;
}

sub expire_session {
    my ($mgr, $session) = @_;
    my $app = MT->instance;
    if ($mgr->{cfg}->{cookies}) {
        my %eat = (
                   -name    => $mgr->{cfg}->{cookie_name},
                   -value   => '',
                   -path    => $mgr->{cfg}->{cookie_path},
                   -expires => '-10y'
        );
        $app->bake_cookie(%eat);
    }
    MT->run_callbacks('SessionExpired', $mgr, $session);
    if ($session) {
        $session->remove
          or return $mgr->error("Couldn't remove session: " . $session->errstr);
    }
    undef;
}

sub purge {    #review. awkward
    my ($mgr, $timeout, $kind) = @_;
    if (ref $mgr) {
        $kind    = $mgr->{cfg}->{kind};
        $timeout = $mgr->{cfg}->{timeout};
    }
    my $t = {start => [0, time - $timeout]};
    $t->{kind} = $kind if defined $kind;
    my @old = MT::Session->load($t, {range => {start => 1}});
    foreach (@old) {
        $_->remove
          or return $mgr->error("Couldn't remove session: " . $_->errstr);
        MT->run_callbacks('SessionExpired', $_);
    }
    1;
}

#--- utility

{
    my @alpha = ('a' .. 'z', 'A' .. 'Z', 0 .. 9);

    sub get_token {
        my $id = join '', map $alpha[rand @alpha], 1 .. 8;
        MT::Session->load($id) ? get_token() : $id;
    }
}

1;

__END__

=head1 NAME

MT::SessionMgr - A encapsulation of routines for easier and
more flexible management of session in Movable Type.

=head1 VERSION

0.31

=head1 SYNOPSIS

=head1 DESCRIPTION

=head1 METHODS

=head1 AUTHOR & COPYRIGHT

MT::SessionMgr is Copyright 2005-2007, Timothy Appnel,
Appnel Internet Solutions, LLC. http://appnel.com/

This program is free software; you can use, modify, and
redistribute it under the same terms as Perl 5.8.x itself.

See http://www.perl.com/perl/misc/Artistic.html

=cut
